http://bit.ly/jmb-cpoa http://iut-blagnac.github.io/cpoa/
Les diagrammes d'états-transitions (plus simplement diagramme d’état) d’UML™ décrivent le comportement interne d’un objet à l’aide d’un automate à états finis.
Les notions importantes de ce diagramme :
événements déclencheurs
Un signal, une invocation de méthode, etc.
Un booléen
Affectation, invocation de méthode
On peut remplacer les actions systématiques des transitions entrantes :
par une transition interne : entry :
permet de spécifier une activité qui s’accomplit quand on entre dans l’état.
permet de spécifier une activité qui s’accomplit quand on sort de l’état.
commence dès que l’activité entry est terminée.
Lorsque cette activité est terminée, une transition
d’achèvement peut être déclenchée.
Si une transition se déclenche pendant que l’activité do
est en cours, cette dernière est interrompue et
l’activité exit de l’état s’exécute.
|
QUESTION
|
Un état peut lui-même être doté d’un comportement et donc représenter à lui seul une machine à état. Par exemple :
|
QUESTION
Intégrez les semestres aux diagramme précédent (étudiants) |
On peut représenter l’évolution de différentes machines de manière concurrente (parallèle). Par exemple :
|
QUESTION
Réalisez le diagramme d’état d’une machine à boisson rendant la monnaie. |
Soit la machine à état suivante :
public void insererPiece() {
if (etat == A_PIECE) {
System.out.println("Vous ne pouvez plus insérer de pièces");
} else if (etat == EPUISE) {
System.out.println("Vous ne pouvez pas insérer de pièce, nous sommes en rupture de stock");
} else if (etat == VENDU) {
System.out.println("Veuillez patienter, le bonbon va tomber");
} else if (etat == SANS_PIECE) {
etat = A_PIECE;
System.out.println("Vous avez inséré une pièce");
}
}
Etat qui contiendra une méthode pour
chaque action
Etat. Elles seront responsable du comportement.
Etape 1 :
Etape 2
public class EtatSansPiece implements Etat {
// Va falloir remplir ici...
public void insererPiece() {
System.out.println("Vous avez inséré une pièce");
// changer d'état si besoin
}
...
}
Etape 3
public class Distributeur {
Etat etat = new EtatSansPiece(); // état initial
...
public void insererPiece() {
etat.insererPiece(); // on délègue à l'état le soin de réagir
}
...
}
Etape 4 (enfin, retour sur l’étape 2) : une solution possible…
public class EtatSansPiece implements Etat {
Distributeur distributeur; // référence au distributeur qu'on gère
public EtatSansPiece(Distributeur distributeur) {
this.distributeur = distributeur;
}
public void insererPiece() {
System.out.println("Vous avez inséré une pièce");
distributeur.setEtat(distributeur.getEtatAPiece());
}
...
}
Etat permet à un objet de modifier son comportement, quand son état interne change. Tout se passe comme si l’objet changeait de classe.
|
QUESTION
Que pensez-vous de notre solution précédente par rapport à ce diagramme UML? |
L’état possède une référence vers le contexte (Distributeur dans notre exemple).
Observateur définit une relation entre objets de type un-à-plusieurs, de façon que, lorsqu’un objet change d’état, tous ceux qui en dépendent en soient notifiés et soient mis à jour automatiquement.
Le patron Observer est utilisable dans de nombreuses situations :
Java fournit des classes Observable/Observer pour le patron Observer.
La classe java.util.Observable est la classe de base pour les sujets.
Ainsi, toute classe qui veut être observée étant cette classe dont
voici les caractéristiques :
L’interface java.util.Observer correspond aux observateurs
qui doivent implémenter cette interface.
Voici la liste des méthodes de java.util.Observable :
1 public Observable()
2 public synchronized void addObserver(Observer o)
3 protected synchronized void setChanged()
4 public synchronized void deleteObserver(Observer o)
5 protected synchronized void clearChanged()
6 public synchronized boolean hasChanged()
7 public void notifyObservers(Object arg)
8 public void notifyObservers()
1 /**
2 * This method is called whenever the observed object is changed. An
3 * application calls an observable object's notifyObservers method to have all
4 * the object's observers notified of the change.
5 *
6 * Parameters:
7 * o - the observable object
8 * arg - an argument passed to the notifyObservers method
9 */
10 public abstract void update(Observable o, Object arg)
On veut pouvoir :
vous avez besoin d’utiliser plusieurs sous-classes existantes, mais l'adaptation de leur interface par dérivation de chacune d’entre elles est impraticable. Un adaptateur objet peut adapter l’interface de sa classe parente.
| Ce dernier cas ne concerne que le cas "adaptateur d’objet" |
public interface Canard {
public void cancaner();
public void voler();
}
public class Colvert implements Canard {
public void cancaner() {
System.out.println("Coincoin");
}
public void voler() {
System.out.println("Je vole");
}
}
public interface Dindon {
public void glouglouter();
public void voler();
}
public class DindonSauvage implements Dindon {
public void glouglouter() {
System.out.println("Glouglou");
}
public void voler() {
System.out.println("Je ne vole pas loin");
}
}
Vous êtes à court d’objets Canard et vous aimeriez utiliser des objets Dindon à la place!
public class AdaptateurDindon implements Canard {
Dindon dindon;
...
public void cancaner() {
dindon.glouglouter();
}
public void voler() {
// Adaptation du vol
for(int i=0; i < 5; i++) {
dindon.voler();
}
}
}
Adaptateur (Adaptor) permet de convertir l’interface d’une classe en une autre conformément à l’attente du client. L’Adaptateur permet à des classes de collaborer, alors qu’elles n’auraient pas pu le faire du fait d’interfaces incompatibles.
Quelques situations à problème :
Adapté d’un exemple tiré de http://www.tutorialspoint.com/design_pattern/visitor_pattern.htm
Définir une interface pour représenter les éléments de la structure.
public interface ComputerPart {
...
}
Anticiper l’utilisation du visiteur.
public interface ComputerPartVisitor {
...
}
public interface ComputerPart {
public void accept(ComputerPartVisitor computerPartVisitor);
}
Créer les classes concrètes qui implémentent l’interface.
public class Keyboard implements ComputerPart {
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
computerPartVisitor.visit(this);
}
}
…
public class Computer implements ComputerPart {
ComputerPart[] parts;
public Computer(){
parts = new ComputerPart[] {new Mouse(), new Keyboard(), new Monitor()};
}
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
for (int i = 0; i < parts.length; i++) {
parts[i].accept(computerPartVisitor);
}
computerPartVisitor.visit(this);
}
}
Définir l’interface pour représenter le visiteur.
public interface ComputerPartVisitor {
public void visit(Computer computer);
public void visit(Mouse mouse);
public void visit(Keyboard keyboard);
public void visit(Monitor monitor);
}
Créer des visiteurs concrets.
public class DisplayVisitor implements ComputerPartVisitor {
@Override
public void visit(Computer computer) {
System.out.println("Displaying Computer.");
}
@Override
public void visit(Mouse mouse) {
System.out.println("Displaying Mouse.");
}
@Override
public void visit(Keyboard keyboard) {
System.out.println("Displaying Keyboard.");
}
@Override
public void visit(Monitor monitor) {
System.out.println("Displaying Monitor.");
}
}
Utiliser le visiteur DisplayVisitor.
public class VisitorPatternDemo {
public static void main(String[] args) {
ComputerPart computer = new Computer();
computer.accept(new DisplayVisitor());
}
}
Verify the output.
Displaying Mouse.
Displaying Keyboard.
Displaying Monitor.
Displaying Computer.
Visiteur (Visitor) permet la représentation d’une opération applicable aux éléments d’une structure d’objet.
Il définit une nouvelle opération, sans qu’il soit nécessaire de modifier la classe des éléments sur lesquels elle agit.
Avantages :
Inconvénients :
| Exemple tiré de ce site. |
public interface ItemElement {
public int accept(ShoppingCartVisitor visitor);
}
public class Book implements ItemElement {
private int price;
private String isbnNumber;
public Book(int cost, String isbn){
this.price=cost;
this.isbnNumber=isbn;
}
public int getPrice() {
return price;
}
public String getIsbnNumber() {
return isbnNumber;
}
@Override
public int accept(ShoppingCartVisitor visitor) {
return visitor.visit(this);
}
}
public class Fruit implements ItemElement {
private int pricePerKg;
private int weight;
private String name;
public Fruit(int priceKg, int wt, String nm){
this.pricePerKg=priceKg;
this.weight=wt;
this.name = nm;
}
public int getPricePerKg() {
return pricePerKg;
}
public int getWeight() {
return weight;
}
public String getName(){
return this.name;
}
@Override
public int accept(ShoppingCartVisitor visitor) {
return visitor.visit(this);
}
}
public interface ShoppingCartVisitor {
int visit(Book book);
int visit(Fruit fruit);
}
public class ShoppingCartVisitorImpl implements ShoppingCartVisitor {
@Override
public int visit(Book book) {
int cost=0;
//apply 5$ discount if book price is greater than 50
if(book.getPrice() > 50){
cost = book.getPrice()-5;
} else cost = book.getPrice();
System.out.println("Book ISBN::"+book.getIsbnNumber() + " cost ="+cost);
return cost;
}
@Override
public int visit(Fruit fruit) {
int cost = fruit.getPricePerKg()*fruit.getWeight();
System.out.println(fruit.getName() + " cost = "+cost);
return cost;
}
}
public class ShoppingCartClient {
public static void main(String[] args) {
ItemElement[] items = new ItemElement[]{new Book(20, "1234"),new Book(100, "5678"),
new Fruit(10, 2, "Banana"), new Fruit(5, 5, "Apple")};
int total = calculatePrice(items);
System.out.println("Total Cost = "+total);
}
private static int calculatePrice(ItemElement[] items) {
ShoppingCartVisitor visitor = new ShoppingCartVisitorImpl();
int sum=0;
for(ItemElement item : items){
sum = sum + item.accept(visitor);
}
return sum;
}
}
Book ISBN::1234 cost =20 Book ISBN::5678 cost =95 Banana cost = 20 Apple cost = 25 Total Cost = 160
On a besoin de références à un objet, qui soient plus créatives et plus sophistiquées qu’un simple pointeur.
Procuration (Proxy) fournit à un tiers un mandataire ou un remplaçant, pour contrôler l’accès à cet objet.
Une référence intelligente est le remplaçant d’un pointeur brut, qui réalise des opérations supplémentaires, lors de l’accès à l’objet. Quelques utilisations typiques sont :
Remote Method Invocation
import java.rmi.*;
public interface MonService extends Remote {
public String direBonjour() throws RemoteException;
}
import java.rmi.*;
import java.rmi.server.*;
public class MonServiceImpl extends UnicastRemoteObject implements MonService {
public String direBonjour() {
return "Le serveur dit 'Bonjour'";
}
public MonServiceImpl() throws RemoteException {}
public static void main (String[] args) {
try {
MonService service = new MonServiceImpl();
Naming.rebind("BonjourDistant", service);
} catch(Exception ex) {
ex.printStackTrace();
}
}
}
MonService service =
(MonService) Naming.lookup("rmi://127.0.0.1/BonjourDistant");
...
service.direBonjour();
On veut pouvoir :
Itérateur (Iterator) fournit un moyen d’accès séquentiel aux éléments d’un agrégat d’objets, sans mettre à découvert la représentation interne de celui-ci.
# Saluer tout le monde
def say_hi
if @names.nil?
puts "..."
elsif @names.respond_to?("each")
# @names est une liste de noms : traitons-les uns par uns
@names.each do |name|
puts "Hello #{name}!"
end
else
puts "Hello #{@names}!"
end
end
On veut pouvoir :
Composite permet de composer des objets en des structures arborescentes pour représenter des hiérarchies composant/composé. Permet au client de traiter d’une façon unique les objets et les combinaisons d’objets.
import java.util.ArrayList;
interface Graphic {
public void print();
}
class CompositeGraphic implements Graphic {
private ArrayList<Graphic> mChildGraphics = new ArrayList<Graphic>();
public void print() {
for (Graphic graphic : mChildGraphics) {
graphic.print();
}
}
public void add(Graphic graphic) {
mChildGraphics.add(graphic);
}
public void remove(Graphic graphic) {
mChildGraphics.remove(graphic);
}
}
Que pensez-vous de cette définition de Composite ?
| On appelle ces modèles des "Patrons abîmés" (anti-patterns). |
Remplacer tous ces switch cases
continuer = true;
while (continuer) {
AAB.afficherMenu(monAg);
choix = lect.next();
choix = choix.toLowerCase();
switch (choix) {
...
case "p" :
System.out.print("Propriétaire -> ");
nom = lect.next();
AAB.comptesDUnPropretaire (monAg, nom);
break;
...
Afficher une liste séparemment du switch
//AAB.afficherMenu(monAg);
System.out.println("Menu de " + ag.getNomAgence() + " (" + ag.getLocAgence() + ")");
System.out.println("l - Liste des comptes de l'agence");
...
System.out.println("p - voir les comptes d'un Propriétaire (par son nom)");
...
System.out.print("Choix -> ");
}
Tester tous les choix pour actionner la bonne option
...
case "p" :
System.out.print("Propriétaire -> ");
nom = lect.next();
AAB.comptesDUnPropretaire (monAg, nom);
break;
...
public interface ActionList extends Action {
public String listTitle();
public int size();
public boolean addAction(Action ac);
public boolean removeAction(Action ac);
public String[] listOfActions() ;
}
public interface Action {
public String actionMessage ();
public void execute(AgenceBancaire ag);
}
public class Action1 implements Action {
private String lineMessage;
...
public String actionMessage() {
return this.lineMessage;
}
public void execute(AgenceBancaire ab) {
...
ab.afficher();
}
}
action.execute(ab);
Action a1 = new Action1("Liste des comptes de l'agence");
Action a2 = new Action2("Voir un compte (par son numéro)");
Action a3 = new Action3(...);
ActionList al1 = new ActionListAgenceBancaire("Menu Général");
al1.addAction(a1);
al1.addAction(a2);
public void execute(AgenceBancaire ab) throws Exception {
...
while (true) {
this.printMenu();
choice = this.readResponse();
...
this.myMenu.get(choice).execute(ab);
...
public interface ActionList extends Action {
}
Quelques conseils :
Les patterns sont des outils, non des règles.
⇒ Rien n’empêche de les modifier et de les adapter à votre problème.
Ne vous emballez pas et recherchez la simplicité.
⇒ Si vous trouvez une solution plus simple que l’emploi d’un pattern, n’hésitez pas !
éliminez ce qui n’est pas vraiment nécessaire.
⇒ N’ayez pas peur de supprimer un design pattern inutile de votre conception.
Il n’y a pas que les 3 types de patrons que l’on a vu :
des exemples?
|
QUESTION
Un ou deux exemples de chaque? |
Il y a par exemple :
Il y en a beaucoup :
⇒ Les 2 prochaines semaines vous allez refactorer une application :
| Ces définitions seront enrichies au fur et à mesure des patrons étudiés. |
Fabrique (simple) définit une interface pour la création d’un objet, mais en laissant à des sous-classes le choix des classes à instancier (voir aussi Fabrique abstraite).
Fabrique (abstraite) fournit une interface pour la création de familles d’objets apparentés ou interdépendants, sans qu’il soit nécessaire de spécifier leurs classes concrètes (voir aussi Fabrique).
cysboy. Disponible ici (le 2016-12-14).Document réalisé par Jean-Michel Bruel via Asciidoctor (version 1.5.5) de 'Dan Allen', lui même basé sur AsciiDoc.
Pour l’instant ce document est libre d’utilisation et géré par la 'Licence Creative Commons'.
licence Creative Commons Paternité - Partage à l'Identique 3.0 non transposé.
/